import Observer from './observer.js'
import compiler from './compiler.js'
import Watcher from './watcher'
import { h } from 'snabbdom'

class Vue {
  constructor (options = {}) {
    this.$options = options
    this.$data = options.data || {}
    this.$render = options.render
    this._c = compiler

    this._proxyData(this.$data)
    new Observer(this.$data)
    this._callHook('created')
    this._wath(options.data)
    options.el && this.$mount(options.el)
    this._callHook('mounted')
  }

  _proxyData (data = {}) {
    Object.keys(data).forEach(key => {
      Object.defineProperty(this, key, {
        enumerable: true,
        configurable: true,
        get () {
          return data[key]
        },
        set (newVal) {
          if (newVal === data[key]) {
            return
          }
          data[key] = newVal
        }
      })
    })
  }

  // 渲染页面
  _compiler (component) {
    this.$vnode = this._c(this.$vnode, component(h, this))
  }

  // 监听数据变化时重新渲染
  _wath (data) {
    Object.keys(data).forEach(key => {
      new Watcher(this, key, () => {
        this.$render(this._compiler.bind(this))
      }, '_compiler')
    })
  }

  // 触发传入 options 的钩子函数
  _callHook (hook) {
    const handler = this.$options[hook]
    if (handler) {
      handler.call(this)
    }
  }

  // 挂载页面
  $mount (el) {
    if (this.$mounted) {
      return
    }
    this.$el = typeof el === 'string' ? document.querySelector(el) : el
    this.$vnode = this.$el
    this.$render(this._compiler.bind(this))
    this.$mounted = true
  }
}

export default Vue
